﻿using System;
using System.Reflection;
using Microsoft.Office.Core;
using System.Diagnostics;

namespace FBProject.BusinessLogic.MSProjectProxies
{
    public class MSProjectProxy
    {
        private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(typeof(MSProjectProxy));

        private Microsoft.Office.Interop.MSProject.Application MSApplication { get; set; }

        private Microsoft.Office.Interop.MSProject._IProjectDoc MSProject { get; set; }

        public MSProjectProxy(Microsoft.Office.Interop.MSProject.Application msApplication)
        {
            MSApplication = msApplication;
            MSProject = MSApplication.ActiveProject;
        }

        private MSProjectState MSProjectState { get; set; }

        private string GetSetting(string settingName)
        {
            return GetSetting(settingName, string.Empty);
        }

        private string GetSetting(string settingName, string defaultValue)
        {
            var settingValue = defaultValue;

            var customDocumentProperties = MSProject.CustomDocumentProperties;
            Type typeCustomDocumentProperties = customDocumentProperties.GetType();

            object customProperty = null;
            try
            {
                customProperty = typeCustomDocumentProperties.InvokeMember(
                    "Item",
                    BindingFlags.Default | BindingFlags.GetProperty,
                    null, 
                    customDocumentProperties,
                    new object[] { settingName });
            }
            catch (TargetInvocationException)
            {
                // Property doesn't exist yet
                return settingValue;
            }

            if (customProperty != null)
            {
                Type typeCustomProperty = customProperty.GetType();
                settingValue = typeCustomProperty.InvokeMember("Value",
                    BindingFlags.Default | BindingFlags.GetProperty,
                    null, 
                    customProperty,
                    new object[] { }).ToString();
            }

            return settingValue;
        }

        private void SaveSetting(string settingName, string settingValue)
        {
            var customDocumentProperties = MSProject.CustomDocumentProperties;
            Type typeCustomDocumentProperties = customDocumentProperties.GetType();

            object customProperty = null;
            try
            {
                customProperty = typeCustomDocumentProperties.InvokeMember(
                    "Item",
                    BindingFlags.Default | BindingFlags.GetProperty,
                    null,
                    customDocumentProperties,
                    new object[] { settingName });
            }
            catch (TargetInvocationException)
            {
                // Property doesn't exist yet
            }

            if (customProperty == null)
            {
                object[] arguments = { 
                                     settingName, 
                                     false, 
                                     MsoDocProperties.msoPropertyTypeString,
                                     settingValue};

                typeCustomDocumentProperties.InvokeMember(
                    "Add", 
                    BindingFlags.Default | BindingFlags.InvokeMethod, 
                    null,
                    customDocumentProperties, 
                    arguments);
            }
            else
            {
                Type typeCustomProperty = customProperty.GetType();
                typeCustomProperty.InvokeMember("Value",
                    BindingFlags.Default | BindingFlags.SetProperty,
                    null, 
                    customProperty,
                    new object[] { settingValue });
            }

        }

        public string FBUrl
        {
            get { return GetSetting("FBUrl"); }
            set { SaveSetting("FBUrl", value); }
        }

        public string FBCaseUrlFormat
        {
            get { return GetSetting("FBCaseUrlFormat", "{0}default.asp?{1}"); }
            set { SaveSetting("FBCaseUrlFormat", value); }
        }

        public string FBCaseNumberFormat
        {
            get { return GetSetting("FBCaseNumberFormat", "FB{0}"); }
            set { SaveSetting("FBCaseNumberFormat", value); }
        }

        public string FBDBConnectionString
        {
            get { return GetSetting("FBDBConnectionString"); }
            set { SaveSetting("FBDBConnectionString", value); }
        }

        public string FBSqlQueryTemplate
        {
            get
            {
                return GetSetting("FBSqlQueryTemplate", @"SELECT
    [Bug].[ixBug] AS 'Case'
    ,[Bug].[sTitle] AS 'Title'
    ,ISNULL([Bug].[hrsCurrEst], 0) AS 'Estimate (current) - Hours'
    ,ISNULL([Bug].[hrsElapsed], 0) AS 'Elapsed Time - Hours'
    ,ISNULL([PersonAssignedTo].[sFullName], '') AS 'Assigned To'
    ,CAST([Bug].[ixPriority] AS nvarchar(5)) + ' - ' + ISNULL([Priority].[sPriority], '') AS 'Priority'
    ,ISNULL([FixFor].[sFixFor], '') AS 'Milestone'
    ,ISNULL([Status].[sStatus], '') AS 'Status'
FROM [Bug] WITH (NOLOCK)
LEFT JOIN [Person] [PersonAssignedTo] WITH (NOLOCK) ON [PersonAssignedTo].[ixPerson] = [Bug].[ixPersonAssignedTo]
LEFT JOIN [Priority] WITH (NOLOCK) ON [Priority].[ixPriority] = [Bug].[ixPriority]
LEFT JOIN [Status] WITH (NOLOCK) ON [Status].[ixStatus] = [Bug].[ixStatus]
LEFT JOIN [FixFor] WITH (NOLOCK) ON [FixFor].[ixFixFor] = [Bug].[ixFixFor]
{0}
WHERE {1}");
            }
            set { SaveSetting("FBSqlQueryTemplate", value); }
        }

        public string FBSqlQueryAdditionalJoins
        {
            get { return GetSetting("FBSqlQueryAdditionalJoins", @"LEFT JOIN [Category] WITH (NOLOCK) ON [Category].[ixCategory] = [Bug].[ixCategory]
LEFT JOIN [Project] WITH (NOLOCK) ON [Project].[ixProject] = [Bug].[ixProject]"); }
            set { SaveSetting("FBSqlQueryAdditionalJoins", value); }
        }

        public string FBSqlQueryWhere
        {
            get { return GetSetting("FBSqlQueryWhere"); }
            set { SaveSetting("FBSqlQueryWhere", value); }
        }

        public void Activate()
        {
            MSApplication.ActiveWindow.Activate();
            MSApplication.Visible = true;
        }

        public void ExpandAllSubtasks()
        {
            if (Tasks.Count > 0)
            {
                Tasks[1].OutlineShowAllTasks();
            }
        }

        public void CollapseSubtasksNotInProgress()
        {
            foreach (var task in Tasks)
            {
                if (task.HasSubcases && (task.IgnoreForComparison || !task.InProgress))
                    task.OutlineHideSubTasks();
            }
        }

        public void GoToFirstUnfinishedTask()
        {
            foreach (var task in Tasks)
            {
                if ((!task.Completed) && (!task.IgnoreForComparison))
                {
                    this.MSApplication.EditGoTo(ID: task.Id);
                    break;
                }
            }
        }

        public void ChangeSheetCellColor(int color)
        {
            this.MSApplication.SelectSheet();
            this.MSApplication.Font32Ex(CellColor: color);
        }

        public void ChangeRowCellColor(int[] rows, int color)
        {
            if (rows.Length > 0)
            {
                foreach (var row in rows)
                {
                    this.MSApplication.EditGoTo(ID: row);
                    this.MSApplication.SelectRow(Row: 0, RowRelative: true);
                    this.MSApplication.Font32Ex(CellColor: color);
                }
            }
        }

        public bool ChangeCellCellColor(int row, string columnName, int color)
        {
            if (GoToTaskColumn(row, columnName))
            {
                this.MSApplication.Font32Ex(CellColor: color);
                return true;
            }
            else
            {
                return false;
            }
        }

        public MSTasksProxy Tasks
        {
            get
            {
                return new MSTasksProxy(this, MSProject.Tasks);
            }
        }

        public string FullName 
        {
            get { return MSProject.FullName; }
        }

        public MSTaskProxy AddTask(ExternalTask externalTask)
        {
            if (externalTask == null)
                throw new ArgumentNullException("externalTask");

            var task = Tasks.Add(externalTask);

            if (!string.IsNullOrEmpty(task.TaskHyperlink))
            {
                InsertHyperlink(task.Id, task.TaskHyperlink, task.TaskNumberFormatted);
            }

            return task;
        }

        private bool InsertHyperlink(int id, string hyperlinkAddress, string hyperlinkName)
        {
            if (GoToTask(id))
            {
                MSApplication.InsertHyperlink(Name: hyperlinkName, Address: hyperlinkAddress);
                return true;
            }
            else
            {
                return false;
            }
        }

        public void SaveStateAndPrepareForSynchronization()
        {
            MSApplication.ScreenUpdating = false;

            MSProjectState = new MSProjectState()
            { 
                CurrentGroup = MSProject.CurrentGroup,
                CurrentFilter = MSProject.CurrentFilter,
                AutoLevel = MSProject.Application.AutoLevel
            };

            MSApplication.AutoLevel = false;
            MSApplication.GroupClear();
            MSApplication.FilterClear();
        }

        public void RestoreStateAfterSynchronization()
        {
            if (MSProjectState != null)
            {
                MSApplication.GroupApply(Name: MSProjectState.CurrentGroup);
                MSApplication.FilterApply(Name: MSProjectState.CurrentFilter);
                MSApplication.AutoLevel = MSProjectState.AutoLevel;
            }

            MSApplication.ScreenUpdating = true;
        }

        public bool GoToTask(int id)
        {
            bool result = false;

            try
            {
                this.MSApplication.EditGoTo(ID: id);
                result = true;
            }
            catch (System.Runtime.InteropServices.COMException ex)
            {
                if (ex.Message != "Project has searched the entire field and did not find a match.")
                {
                    throw;
                }
            }

            if (!result)
            {
                var task = Tasks[id];
                if (task != null)
                {
                    var originalScreenUpdating = MSApplication.ScreenUpdating;
                    MSApplication.ScreenUpdating = false;

                    Debug.Assert(task.Id == id);
                    var parentTask = task.OutlineParent as Microsoft.Office.Interop.MSProject.Task;
                    while ((parentTask != null) && (parentTask.ID != 0))
                    {
                        parentTask.OutlineShowSubTasks();
                        parentTask = parentTask.OutlineParent as Microsoft.Office.Interop.MSProject.Task;
                    }
                    result = this.MSApplication.EditGoTo(ID: id);

                    MSApplication.ScreenUpdating = originalScreenUpdating;
                }
                else
                {
                    Logger.WarnFormat("GoToTask - Cannot find task with id={0} in Tasks collection.", id);
                }
            }

            return result;
        }

        public bool GoToTaskColumn(int id, string column)
        {
            if (GoToTask(id))
            {
                try
                {
                    this.MSApplication.SelectTaskField(Row: 0, Column: column, RowRelative: true);
                    return true;
                }
                catch (System.Runtime.InteropServices.COMException ex)
                {
                    if (ex.ErrorCode == 1101)
                    {
                        Logger.DebugFormat("GoToTaskColumn() - Column {0} is not visible in the MS Project", column);
                    }
                    throw;
                }
            }
            return false;
        }
    }
}
